window.owl = owl || {};
owl.components = {};
owl.utils = {
    /** 用于检查给定的参数是否为一个纯粹的对象 */
    isPlainObject: function (obj) {
        // 通过 toString 方法判断对象是否为 [object Object]
        if (typeof obj !== 'object' || obj === null || obj.nodeType || obj === obj.window) {
            return false;
        }
        // 通过 constructor 属性判断对象是否由 Object 构造函数创建（纯粹对象）
        if (obj.constructor && !Object.prototype.hasOwnProperty.call(obj.constructor.prototype, 'isPrototypeOf')) {
            return false;
        }
        return true;
    },
    /** 参考 jQuery.extend 方法，使用 defineMethod 处理 Function，实现类的继承方法可以通过 callParent 调用父类的方法 */
    apply: function () {
        let options;
        let name;
        let src;
        let copy;
        let copyIsArray;
        let clone;
        let target = arguments[0] || {};
        let i = 1;
        const length = arguments.length;
        let deep = false;
        if (typeof target === 'boolean') {
            deep = target;
            target = arguments[1] || {};
            i = 2;
        }
        if (typeof target !== 'object' && Object.prototype.toString.call(target) !== '[object Function]') {
            target = {};
        }
        if (length === i) {
            target = this;
            --i;
        }
        for (; i < length; i++) {
            if ((options = arguments[i]) != null) {
                for (name in options) {
                    if (name && name[0] === '$') {
                        continue;
                    }
                    src = target[name];
                    copy = options[name];
                    if (name === 'methods') {
                        copy.$class = target.$class;
                    }
                    if (target === copy) {
                        continue;
                    }
                    if (deep && copy && (owl.utils.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) {
                        if (copyIsArray) {
                            copyIsArray = false;
                            clone = src && Array.isArray(src) ? owl.utils.apply(deep, [], src) : [];
                        } else {
                            clone = src && owl.utils.isPlainObject(src) ? owl.utils.apply(deep, {}, src) : {};
                        }
                        if (copy.$class) {
                            clone.$class = copy.$class;
                        }
                        target[name] = owl.utils.apply(deep, clone, copy);
                    } else if (copy !== undefined) {
                        if (copy instanceof Function) {
                            this.defineMethod(target, name, copy);
                        } else {
                            target[name] = copy;
                        }
                    }
                }
            }
        }
        return target;
    },
    /** 定义方法 */
    defineMethod: function (owner, name, body) {
        const clone = function (method) {
            let prop;
            const newMethod = function () {
                return method.apply(this, arguments);
            };
            for (prop in method) {
                if (Object.hasOwnProperty.call(method, prop)) {
                    newMethod[prop] = method[prop];
                }
            }
            return newMethod;
        };
        if (body.$class) {
            const origin = body;
            body = clone(body);
            body.$origin = origin;
        }
        owner[name] = body;
        body.$name = name;
        body.$class = owner.$class;
    },
}
owl.define = function (name, def) {
    const clz = function (props, env, node) {
        this.props = props;
        this.env = env;
        this.__owl__ = node;
    };
    clz.prototype = new owl.Component();
    clz.prototype.constructor = clz;
    const prop = clz.prototype;
    prop.$class = clz;
    clz.$bases = [];
    clz.components = owl.components;
    prop.setup = function () { }
    prop.render = function (deep = false) {
        this.__owl__.render(deep === true);
    }
    const addBase = function (b) {
        const base = owl.components[b];
        if (base) {
            clz.$bases.push(base);
            owl.utils.apply(true, prop, base.prototype);
            if (base.template) {
                clz.template = base.template;
            }
        } else {
            throw new Error(name + ' 不能扩展未定义的 ' + b);
        }
    };
    if (typeof def.extends === 'string') {
        addBase(def.extends);
    } else if (Array.isArray(def.extends)) {
        for (let i = def.extends.length - 1; i >= 0; i--) {
            const b = def.extends[i];
            if (typeof b === 'string') {
                addBase(b);
            }
        }
    }
    if (def.template) {
        clz.template = def.template;
        delete def.template;
    }
    owl.utils.apply(prop, def);
    prop.callSuper = function () {
        if (!this.$super) {
            this.$super = {};
        }
        const stack = new Error().stack.split('\n')[2].trim().split(/\s+/)[1].trim().split('.');
        const name = stack[stack.length - 1];
        const bases = this.$super[name]?.$bases || this.$class.$bases;
        for (let i = bases.length - 1; i >= 0; i--) {
            let baseMethod = bases[i].prototype[name];
            if (baseMethod) {
                const sp = this.$super[name];
                this.$super[name] = bases[i];
                if (baseMethod.$origin) {
                    this.$super[name] = baseMethod.$origin.$owner;
                    baseMethod = baseMethod.$origin;
                }
                const result = baseMethod.apply(this, arguments);
                this.$super[name] = sp;
                return result;
            }
        }
    };
    clz.$name = name;
    owl.components[name] = clz;
    return clz;
}